using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Assignment2.Actions;
using Assignment2.States;
using Assignment2.Transitions;

namespace Assignment2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;
        bool isPause;

        //world variables
        Integer idleTime,
            hungryValue,
            boringValue,
            toyValue,
            foodValue;

        State initialState;
        State currentState;

        //Set up for button on screen;
        enum BState
        {
            HOVER,
            UP,
            JUST_RELEASED,
            DOWN
        }
        const int NUMBER_OF_BUTTONS = 3,
            START_BUTTON_INDEX = 0,
            FEED_BUTTON_INDEX = 1,
            TOY_BUTTON_INDEX = 2,
            BUTTON_HEIGHT = 100,
            BUTTON_WIDTH = 200;

        Color[] button_color = new Color[NUMBER_OF_BUTTONS];
        Rectangle[] button_rectangle = new Rectangle[NUMBER_OF_BUTTONS];
        BState[] button_state = new BState[NUMBER_OF_BUTTONS];
        Texture2D[] button_texture = new Texture2D[NUMBER_OF_BUTTONS];
        double[] button_timer = new double[NUMBER_OF_BUTTONS];
        //mouse pressed and mouse just pressed
        bool mpressed, prev_mpressed = false;
        //mouse location in window
        int mx, my;
        double frame_time;

        //Graphic variables
        Texture2D playTile;
        Texture2D sheep;
        AnimatedSprite animatedSprite;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 720;
            graphics.PreferredBackBufferWidth = 1280;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            isPause = true;
            //Initialize variables
            idleTime = new Integer(0);
            hungryValue = new Integer(0);
            boringValue = new Integer(0);
            toyValue = new Integer(0);
            foodValue = new Integer(0);

            //Initialize states and transitions
            StateAndTransitionPool.Dead = new DeadState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.ToDead = new ToDeadTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Hungry = new HungryState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Boring = new BoringState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Eating = new EatingState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Playing = new PlayingState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime); ;
            StateAndTransitionPool.HungryToEating = new HungryToEatingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.BoringToPlaying = new BoringToPlayingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.WaitingToHungry = new WaitingToHungryTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.WaitingToBoring = new WaitingToBoringTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Waiting = new WaitingState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.Sleeping = new SleepingState(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.SleepingToWaiting = new SleepingToWaitingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.WaitingToSleeping = new WaitingToSleepingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.PlayingToWaiting = new PlayingToWaitingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);
            StateAndTransitionPool.EatingToWaiting = new EatingToWaitingTransition(ref boringValue, ref hungryValue, ref toyValue, ref foodValue, ref idleTime);

            StateAndTransitionPool.ToDead.updateMember();
            StateAndTransitionPool.Hungry.updateMember();
            StateAndTransitionPool.Boring.updateMember();
            StateAndTransitionPool.Eating.updateMember();
            StateAndTransitionPool.Playing.updateMember();
            StateAndTransitionPool.HungryToEating.updateMember();
            StateAndTransitionPool.BoringToPlaying.updateMember();
            StateAndTransitionPool.WaitingToHungry.updateMember();
            StateAndTransitionPool.WaitingToBoring.updateMember();
            StateAndTransitionPool.Waiting.updateMember();
            StateAndTransitionPool.Sleeping.updateMember();
            StateAndTransitionPool.SleepingToWaiting.updateMember();
            StateAndTransitionPool.WaitingToSleeping.updateMember();
            StateAndTransitionPool.PlayingToWaiting.updateMember();
            StateAndTransitionPool.EatingToWaiting.updateMember();



            initialState = StateAndTransitionPool.Waiting;

            currentState = initialState;


            //For the buttons

            // starting x and y locations to stack buttons 
            // vertically in the middle of the screen
            int x = Window.ClientBounds.Width - BUTTON_WIDTH - 20;
            int y = Window.ClientBounds.Height / 2 -
                NUMBER_OF_BUTTONS / 2 * BUTTON_HEIGHT -
                (NUMBER_OF_BUTTONS % 2) * BUTTON_HEIGHT / 2;
            for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
            {
                button_state[i] = BState.UP;
                button_color[i] = Color.White;
                button_timer[i] = 0.0;
                button_rectangle[i] = new Rectangle(x, y, BUTTON_WIDTH, BUTTON_HEIGHT);
                y += BUTTON_HEIGHT + 10;
            }
            IsMouseVisible = true;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            button_texture[START_BUTTON_INDEX] =
                Content.Load<Texture2D>("Start");
            button_texture[FEED_BUTTON_INDEX] =
                Content.Load<Texture2D>("Feed");
            button_texture[TOY_BUTTON_INDEX] =
                Content.Load<Texture2D>("GiveToys");

            playTile = Content.Load<Texture2D>("PlayTiles");
            sheep = Content.Load<Texture2D>("Sheep1");
            animatedSprite = new AnimatedSprite(sheep, 1, 20);

            font = Content.Load<SpriteFont>("font");
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            if (currentState == StateAndTransitionPool.Dead)
            {
                isPause = true;
            }

            //Assume no transition is triggered
            Transition triggeredTransition = null;

            if (!isPause)
            {
                //Check through each transition and store the first
                //one that triggers.
                foreach (Transition transition in currentState.getTransitions())
                {
                    if (transition.isTriggered())
                    {
                        triggeredTransition = transition;
                        break;
                    }
                }

                State targetState;
                List<Action> actions;
                //Check if we have a transition to fire
                if (triggeredTransition != null)
                {
                    //Find the target State
                    targetState = triggeredTransition.getTargetState();
                    //Add the exit action of the old state, the
                    //transition action and the entry for the new state.
                    actions = currentState.getExitAction();
                    actions.AddRange(triggeredTransition.getAction());
                    actions.AddRange(targetState.getEntryAction());

                    currentState = targetState;
                }
                else
                {
                    actions = currentState.getAction();
                }

                foreach (Action action in actions)
                {
                    action.DoAction();
                }

                animatedSprite.Update();
            }

            // update mouse variables
            MouseState mouse_state = Mouse.GetState();
            mx = mouse_state.X;
            my = mouse_state.Y;
            prev_mpressed = mpressed;
            mpressed = mouse_state.LeftButton == ButtonState.Pressed;

            update_buttons();



            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.DrawString(font, "Amount of Toy: " + toyValue, new Vector2(100, 100), Color.White);
            spriteBatch.DrawString(font, "Amount of Food: " + foodValue, new Vector2(100, 120), Color.White);
            spriteBatch.DrawString(font, "Hungry Level: " + hungryValue, new Vector2(100, 140), Color.White);
            spriteBatch.DrawString(font, "Boring Level: " + boringValue, new Vector2(100, 160), Color.White);
            spriteBatch.DrawString(font, "Idle : " + idleTime, new Vector2(100, 180), Color.White);
            spriteBatch.DrawString(font, "Current State: " + currentState.ToString(), new Vector2(100, 200), Color.White);

            for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
            {
                spriteBatch.Draw(button_texture[i], button_rectangle[i], button_color[i]);
            }



            //spriteBatch.Draw(sheep, new Vector2(640, 400),null, Color.White,0,new Vector2(500,500),0.1f,SpriteEffects.None,0);
            //for (int i = 0; i < 3; i++)
            //{
            //    for (int j = 0; j < 3; j++)
            //    {
            //        spriteBatch.Draw(playTile, new Vector2(340 + j * 200, 60 + i * 200), Color.White);
            //    }
            //}
            spriteBatch.End();
            if (currentState is WaitingState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 0);
            if (currentState is SleepingState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 1);
            if (currentState is PlayingState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 2);
            if (currentState is EatingState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 3);
            if (currentState is HungryState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 4);
            if (currentState is BoringState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 5);
            if (currentState is DeadState)
                animatedSprite.Draw(spriteBatch, new Vector2(640, 400), 6);

            base.Draw(gameTime);
        }

        // wrapper for hit_image_alpha taking Rectangle and Texture
        Boolean hit_image_alpha(Rectangle rect, Texture2D tex, int x, int y)
        {
            return hit_image_alpha(0, 0, tex, tex.Width * (x - rect.X) /
                rect.Width, tex.Height * (y - rect.Y) / rect.Height);
        }

        // wraps hit_image then determines if hit a transparent part of image 
        Boolean hit_image_alpha(float tx, float ty, Texture2D tex, int x, int y)
        {
            if (hit_image(tx, ty, tex, x, y))
            {
                uint[] data = new uint[tex.Width * tex.Height];
                tex.GetData<uint>(data);
                if ((x - (int)tx) + (y - (int)ty) *
                    tex.Width < tex.Width * tex.Height)
                {
                    return ((data[
                        (x - (int)tx) + (y - (int)ty) * tex.Width
                        ] &
                                0xFF000000) >> 24) > 20;
                }
            }
            return false;
        }

        // determine if x,y is within rectangle formed by texture located at tx,ty
        Boolean hit_image(float tx, float ty, Texture2D tex, int x, int y)
        {
            return (x >= tx &&
                x <= tx + tex.Width &&
                y >= ty &&
                y <= ty + tex.Height);
        }

        // determine state and color of button
        void update_buttons()
        {
            for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
            {

                if (hit_image_alpha(
                    button_rectangle[i], button_texture[i], mx, my))
                {
                    button_timer[i] = 0.0;
                    if (mpressed)
                    {
                        // mouse is currently down
                        button_state[i] = BState.DOWN;
                        button_color[i] = Color.Blue;
                    }
                    else if (!mpressed && prev_mpressed)
                    {
                        // mouse was just released
                        if (button_state[i] == BState.DOWN)
                        {
                            // button i was just down
                            button_state[i] = BState.JUST_RELEASED;
                        }
                    }
                    else
                    {
                        button_state[i] = BState.HOVER;
                        button_color[i] = Color.LightBlue;
                    }
                }
                else
                {
                    button_state[i] = BState.UP;
                    if (button_timer[i] > 0)
                    {
                        button_timer[i] = button_timer[i] - frame_time;
                    }
                    else
                    {
                        button_color[i] = Color.White;
                    }
                }

                if (button_state[i] == BState.JUST_RELEASED)
                {
                    take_action_on_button(i);
                }
            }
        }


        // Logic for each button click goes here
        void take_action_on_button(int i)
        {
            idleTime.Value = 0;
            //take action corresponding to which button was clicked
            switch (i)
            {
                case START_BUTTON_INDEX:
                    isPause = !isPause;
                    if (currentState == StateAndTransitionPool.Dead)
                    {
                        currentState = StateAndTransitionPool.Waiting;
                        foodValue.Value = 0;
                        toyValue.Value = 0;
                        idleTime.Value = 0;
                        hungryValue.Value = 0;
                        boringValue.Value = 0;
                    }
                    break;
                case FEED_BUTTON_INDEX:
                    foodValue.Value = foodValue.Value + 500;
                    break;
                case TOY_BUTTON_INDEX:
                    toyValue.Value = toyValue.Value + 500;
                    break;
                default:
                    break;
            }
        }
    }
}
